import 'dart:async';

import 'package:flutter/foundation.dart';

import 'joke.dart';

/// Exposes the state required by the UI.
class RandomJokeState {
  const RandomJokeState({
    this.joke,
    this.error,
    this.isLoading = false,
    this.isRefreshing = false,
  });

  final Joke? joke;
  final Object? error;
  final bool isLoading;
  final bool isRefreshing;

  bool get hasError => error != null;
  bool get hasJoke => joke != null;

  RandomJokeState copyWith({
    Joke? joke,
    Object? error,
    bool? isLoading,
    bool? isRefreshing,
  }) {
    return RandomJokeState(
      joke: joke ?? this.joke,
      error: error ?? this.error,
      isLoading: isLoading ?? this.isLoading,
      isRefreshing: isRefreshing ?? this.isRefreshing,
    );
  }
}

/// Coordinates fetching jokes and exposes the resulting [RandomJokeState].
class RandomJokeController extends ChangeNotifier {
  RandomJokeController({required Future<Joke> Function() fetchJoke})
      : _fetchJoke = fetchJoke {
    unawaited(refresh());
  }

  final Future<Joke> Function() _fetchJoke;
  RandomJokeState _state = const RandomJokeState(isLoading: true);

  RandomJokeState get state => _state;

  Future<void> refresh() {
    final keepPrevious = _state.hasJoke;
    _updateState(
      isLoading: !keepPrevious,
      isRefreshing: keepPrevious,
      error: null,
      // keep existing joke when refreshing
    );

    return _fetch(keepPrevious: keepPrevious);
  }

  Future<void> _fetch({required bool keepPrevious}) async {
    try {
      final joke = await _fetchJoke();
      _updateState(
        joke: joke,
        error: null,
        isLoading: false,
        isRefreshing: false,
      );
    } catch (err, _) {
      _updateState(
        joke: keepPrevious ? _state.joke : null,
        error: err,
        isLoading: false,
        isRefreshing: false,
      );
    }
  }

  void _updateState({
    Joke? joke,
    Object? error,
    bool? isLoading,
    bool? isRefreshing,
  }) {
    _state = _state.copyWith(
      joke: joke,
      error: error,
      isLoading: isLoading,
      isRefreshing: isRefreshing,
    );
    notifyListeners();
  }
}
